1 using UnityEngine;
2 using
UnityEditor;
3 using
System.Collections;
4 using
System.Linq;
5 using
System.Collections.Generic;
6 using
System.IO;
7
8
9
10 [CustomEditor(
typeof( GoDummyPath ) )]
11 public
class GoDummyPathEditor : Editor
12 {
13     
private GoDummyPath _target;
14     
private GUIStyle _labelStyle;
15     
private GUIStyle _indexStyle;
16     
17     
private int _insertIndex = 0;
18     
private float _snapDistance = 5f;
19     
private bool _showNodeDetails;
20     
private bool _fileLoadSaveDetails;
21     
private int _selectedNodeIndex = -1;
22     
23     
24     
#region Monobehaviour and Editor
25     
26     
void OnEnable()
27     {
28         
// setup the font for the 'begin' 'end' text
29         _labelStyle =
new GUIStyle();
30         _labelStyle.fontStyle = FontStyle.Bold;
31         _labelStyle.normal.textColor = Color.white;
32         _labelStyle.fontSize =
16;
33         
34         _indexStyle =
new GUIStyle();
35         _indexStyle.fontStyle = FontStyle.Bold;
36         _indexStyle.normal.textColor = Color.white;
37         _indexStyle.fontSize =
12;
38         
39         _target = (GoDummyPath)target;
40     }
41     
42     
43     
public override void OnInspectorGUI()
44     {
45         
// what kind of handles shall we use?
46         EditorGUILayout.BeginHorizontal();
47         EditorGUILayout.PrefixLabel(
"Use Standard Handles" );
48         _target.useStandardHandles = EditorGUILayout.Toggle( _target.useStandardHandles );
49         EditorGUILayout.EndHorizontal();
50
51         
52         
// path name:
53         EditorGUILayout.BeginHorizontal();
54         EditorGUILayout.PrefixLabel(
"Route Name" );
55         _target.pathName = EditorGUILayout.TextField( _target.pathName );
56         EditorGUILayout.EndHorizontal();
57         
58         
if( _target.pathName == string.Empty )
59             _target.pathName =
"route" + Random.Range( 1, 100000 );
60         
61         
62         
// path color:
63         EditorGUILayout.BeginHorizontal();
64         EditorGUILayout.PrefixLabel(
"Route Color" );
65         _target.pathColor = EditorGUILayout.ColorField( _target.pathColor );
66         EditorGUILayout.EndHorizontal();
67         
68         
69         
// force straight lines:
70         EditorGUILayout.BeginHorizontal();
71         EditorGUILayout.PrefixLabel(
"Force Straight Line Path" );
72         _target.forceStraightLinePath = EditorGUILayout.Toggle( _target.forceStraightLinePath );
73         EditorGUILayout.EndHorizontal();
74         
75         
76         
// resolution
77         EditorGUILayout.BeginHorizontal();
78         EditorGUILayout.PrefixLabel(
"Editor Drawing Resolution" );
79         _target.pathResolution = EditorGUILayout.IntSlider( _target.pathResolution,
2, 100 );
80         EditorGUILayout.EndHorizontal();
81
82         
83         EditorGUILayout.Separator();
84         
85         
86         
// insert node - we need 3 or more nodes for insert to make sense
87         
if( _target.nodes.Count > 2 )
88         {
89             EditorGUILayout.BeginHorizontal();
90             EditorGUILayout.PrefixLabel(
"Insert Node" );
91             _insertIndex = EditorGUILayout.IntField( _insertIndex );
92             
if( GUILayout.Button( "Insert" ) )
93             {
94                 
// validate the index
95                 
if( _insertIndex >= 0 && _insertIndex < _target.nodes.Count )
96                 {
97                     
// insert the node offsetting it a bit from the previous node
98                     
var copyNodeIndex = _insertIndex == 0 ? 0 : _insertIndex;
99                     
var copyNode = _target.nodes[copyNodeIndex];
100                     copyNode.x +=
10;
101                     copyNode.z +=
10;
102                     
103                     insertNodeAtIndex( copyNode, _insertIndex );
104                 }
105             }
106             EditorGUILayout.EndHorizontal();
107         }
108
109
110         
// close route?
111         
if( GUILayout.Button( "Close Path" ) )
112         {
113             Undo.RecordObject( _target,
"Path Vector Changed" );
114             closeRoute();
115             GUI.changed =
true;
116         }
117         
118         
119         
// shift the start point to the origin
120         
if( GUILayout.Button( "Shift Path to Start at Origin" ) )
121         {
122             Undo.RecordObject( _target,
"Path Vector Changed" );
123             
124             
var offset = Vector3.zero;
125             
126             
// see what kind of path we are. the simplest case is just a straight line
127             
var path = new GoSpline( _target.nodes, _target.forceStraightLinePath );
128             
if( path.splineType == GoSplineType.StraightLine || _target.nodes.Count < 5 )
129                 offset = Vector3.zero - _target.nodes[
0];
130             
else
131                 offset = Vector3.zero - _target.nodes[
1];
132             
133             
for( var i = 0; i < _target.nodes.Count; i++ )
134                 _target.nodes[i] += offset;
135             
136             GUI.changed =
true;
137         }
138         
139         
140         
// reverse
141         
if( GUILayout.Button( "Reverse Path" ) )
142         {
143             Undo.RecordObject( _target,
"Path Vector Changed" );
144             _target.nodes.Reverse();
145             GUI.changed =
true;
146         }
147         
148         
149         
// persist to disk
150         EditorGUILayout.Space();
151         EditorGUILayout.LabelField(
"Save to/Read from Disk" );
152
153         EditorGUILayout.Space();
154         EditorGUILayout.BeginHorizontal();
155         EditorGUILayout.PrefixLabel(
"Serialize and Save Path" );
156         
if( GUILayout.Button( "Save" ) )
157         {
158             
var path = EditorUtility.SaveFilePanel( "Save path", Application.dataPath + "/StreamingAssets", _target.pathName + ".asset", "asset" );
159             
if( path != string.Empty )
160             {
161                 persistRouteToDisk( path );
162                 
163                 
// fetch the filename and set it as the routeName
164                 _target.pathName = Path.GetFileName( path ).Replace(
".asset", string.Empty );
165                 GUI.changed =
true;
166             }
167         }
168         EditorGUILayout.EndHorizontal();
169
170
171         
// load from disk
172         EditorGUILayout.BeginHorizontal();
173         EditorGUILayout.PrefixLabel(
"Load saved path" );
174         
if( GUILayout.Button( "Load" ) )
175         {
176             
var path = EditorUtility.OpenFilePanel( "Choose path to load", Path.Combine( Application.dataPath, "StreamingAssets" ), "asset" );
177             
if( path != string.Empty )
178             {
179                 
if( !File.Exists( path ) )
180                 {
181                     EditorUtility.DisplayDialog(
"File does not exist", "Path couldn't find the file you specified", "Close" );
182                 }
183                 
else
184                 {
185                     _target.nodes = GoSpline.bytesToVector3List( File.ReadAllBytes( path ) );
186                     _target.pathName = Path.GetFileName( path ).Replace(
".asset", string.Empty );
187                     GUI.changed =
true;
188                 }
189             }
190         }
191         EditorGUILayout.EndHorizontal();
192
193                 
194         
// node display
195         EditorGUILayout.Space();
196         _showNodeDetails = EditorGUILayout.Foldout( _showNodeDetails,
"Show Node Values" );
197         
if( _showNodeDetails )
198         {
199             EditorGUI.indentLevel++;
200             
for( int i = 0; i < _target.nodes.Count; i++ )
201                 _target.nodes[i] = EditorGUILayout.Vector3Field(
"Node " + ( i + 1 ), _target.nodes[i] );
202             EditorGUI.indentLevel--;
203         }
204         
205         
206         
// instructions
207         EditorGUILayout.Space();
208         EditorGUILayout.HelpBox(
"While dragging a node, hold down Ctrl and slowly move the cursor to snap to a nearby point\n\n" +
209                        
"Click the 'Close Path' button to add a new node that will close out the current path.\n\n" +
210                        
"Hold Command while dragging a node to snap in 5 point increments\n\n" +
211                        
"Double click to add a new node at the end of the path\n\n" +
212                        
"Hold down alt while adding a node to prepend the new node at the front of the route\n\n" +
213                        
"Press delete or backspace to delete the selected node\n\n" +
214                        
"NOTE: make sure you have the pan tool selected while editing paths", MessageType.None );
215
216         
217         
// update and redraw:
218         
if( GUI.changed )
219         {
220             EditorUtility.SetDirty( _target );
221             Repaint();
222         }
223     }
224     
225     
226     
void OnSceneGUI()
227     {
228         
if( !_target.gameObject.activeSelf )
229             
return;
230         
231         
// handle current selection and node addition via double click or ctrl click
232         
if( Event.current.type == EventType.mouseDown )
233         {
234             
var nearestIndex = getNearestNodeForMousePosition( Event.current.mousePosition );
235             _selectedNodeIndex = nearestIndex;
236             
237             
// double click to add
238             
if( Event.current.clickCount > 1 )
239             {
240                 
var translatedPoint = HandleUtility.GUIPointToWorldRay( Event.current.mousePosition )
241                         .GetPoint( ( _target.transform.position - Camera.current.transform.position ).magnitude );
242                 
243                 Undo.RecordObject( _target,
"Path Node Added" );
244                 
245                 
// if alt is down then prepend the node to the beginning
246                 
if( Event.current.alt )
247                     insertNodeAtIndex( translatedPoint,
0 );
248                 
else
249                     appendNodeAtPoint( translatedPoint );
250             }
251         }
252         
253
254         
if( _selectedNodeIndex >= 0 )
255         {
256             
// shall we delete the selected node?
257             
if( Event.current.keyCode == KeyCode.Delete || Event.current.keyCode == KeyCode.Backspace )
258             {
259                 
if (_target.nodes.Count > 2) {
260                     Undo.RecordObject( _target,
"Path Node Deleted" );
261                     Event.current.Use();
262                     removeNodeAtIndex( _selectedNodeIndex );
263                     _selectedNodeIndex = -
1;
264                 }
265             }
266         }
267         
268         
269         
if( _target.nodes.Count > 1 )
270         {
271             
// allow path adjustment undo:
272             Undo.RecordObject( _target,
"Path Vector Changed" );
273             
274             
// path begin and end labels or just one if the path is closed
275             
if( Vector3.Distance( _target.nodes[0], _target.nodes[_target.nodes.Count - 1] ) == 0 )
276             {
277                 Handles.Label( _target.nodes[
0], " Begin and End", _labelStyle );
278             }
279             
else
280             {
281                 Handles.Label( _target.nodes[
0], " Begin", _labelStyle );
282                 Handles.Label( _target.nodes[_target.nodes.Count -
1], " End", _labelStyle );
283             }
284             
285             
// draw the handles, arrows and lines
286             drawRoute();
287             
288             
for( var i = 0; i < _target.nodes.Count; i++ )
289             {
290                 Handles.color = _target.pathColor;
291                 
292                 
// dont label the first and last nodes
293                 
if( i > 0 && i < _target.nodes.Count - 1 )
294                     Handles.Label( _target.nodes[i] +
new Vector3( 3f, 0, 1.5f ), i.ToString(), _indexStyle );
295                 
296                 Handles.color = Color.white;
297                 
if( _target.useStandardHandles )
298                 {
299                     _target.nodes[i] = Handles.PositionHandle( _target.nodes[i], Quaternion.identity );
300                 }
301                 
else
302                 {
303                     
// how big shall we draw the handles?
304                     
var distanceToTarget = Vector3.Distance( SceneView.lastActiveSceneView.camera.transform.position, _target.transform.position );
305                     distanceToTarget = Mathf.Abs( distanceToTarget );
306                     
var handleSize = Mathf.Ceil( distanceToTarget / 75 );
307                     
308                     _target.nodes[i] = Handles.FreeMoveHandle( _target.nodes[i],
309                                             Quaternion.identity,
310                                             handleSize,
311                                             
new Vector3( 5, 0, 5 ),
312                                             Handles.SphereCap );
313                 }
314                 
315
316                 
// should we snap? we need at least 4 nodes because we dont snap to the previous and next nodes
317                 
if( Event.current.control && _target.nodes.Count > 3 )
318                 {
319                     
// dont even bother checking for snapping to the previous/next nodes
320                     
var index = getNearestNode( _target.nodes[i], i, i + 1, i - 1 );
321                     
var nearest = _target.nodes[index];
322                     
var distanceToNearestNode = Vector3.Distance( nearest, _target.nodes[i] );
323                     
324                     
// is it close enough to snap?
325                     
if( distanceToNearestNode <= _snapDistance )
326                     {
327                         GUI.changed =
true;
328                         _target.nodes[i] = nearest;
329                     }
330                     
else if( distanceToNearestNode <= _snapDistance * 2 )
331                     {
332                         
// show which nodes are getting close enough to snap to
333                         
var color = Color.red;
334                         color.a =
0.3f;
335                         Handles.color = color;
336                         Handles.SphereCap(
0, _target.nodes[i], Quaternion.identity, _snapDistance * 2 );
337                         
//Handles.DrawWireDisc( _target.nodes[i], Vector3.up, _snapDistance );
338                         Handles.color = Color.white;
339                     }
340                 }
341             }
// end for
342
343             
344             
if( GUI.changed )
345             {
346                 Repaint();
347                 EditorUtility.SetDirty( _target );
348             }
349         }
// end if
350     }
351     
352     
#endregion
353     
354     
355     
#region Private methods
356     
357     
private void appendNodeAtPoint( Vector3 node )
358     {
359         _target.nodes.Add( node );
360         
361         GUI.changed =
true;
362     }
363     
364     
365     
private void removeNodeAtIndex( int index )
366     {
367         
if( index >= _target.nodes.Count || index < 0 )
368             
return;
369         
370         _target.nodes.RemoveAt( index );
371         
372         GUI.changed =
true;
373     }
374     
375     
376     
private void insertNodeAtIndex( Vector3 node, int index )
377     {
378         
// validate the index
379         
if( index >= 0 && index < _target.nodes.Count )
380         {
381             _target.nodes.Insert( index, node );
382             
383             GUI.changed =
true;
384         }
385     }
386     
387     
388     
private void drawArrowBetweenPoints( Vector3 point1, Vector3 point2 )
389     {
390         
// no need to draw arrows for tiny segments
391         
var distance = Vector3.Distance( point1, point2 );
392         
if( distance < 40 )
393             
return;
394         
395         
// we dont want to be exactly in the middle so we offset the length of the arrow
396         
var lerpModifier = ( distance * 0.5f - 25 ) / distance;
397         
398         Handles.color = _target.pathColor;
399         
400         
// get the midpoint between the 2 points
401         
var dir = Vector3.Lerp( point1, point2, lerpModifier );
402         
var quat = Quaternion.LookRotation( point2 - point1 );
403         Handles.ArrowCap(
0, dir, quat, 25 );
404         
405         Handles.color = Color.white;
406     }
407     
408     
409     
private int getNearestNode( Vector3 pos, params int[] excludeNodes )
410     {
411         
var excludeNodesList = new System.Collections.Generic.List<int>( excludeNodes );
412         
var bestDistance = float.MaxValue;
413         
var index = -1;
414         
415         
var distance = float.MaxValue;
416         
for( var i = _target.nodes.Count - 1; i >= 0; i-- )
417         {
418             
if( excludeNodesList.Contains( i ) )
419                 
continue;
420
421             distance = Vector3.Distance( pos, _target.nodes[i] );
422             
if( distance < bestDistance )
423             {
424                 bestDistance = distance;
425                 index = i;
426             }
427         }
428         
return index;
429     }
430     
431     
432     
private int getNearestNodeForMousePosition( Vector3 mousePos )
433     {
434         
var bestDistance = float.MaxValue;
435         
var index = -1;
436         
437         
var distance = float.MaxValue;
438         
for( var i = _target.nodes.Count - 1; i >= 0; i-- )
439         {
440             
var nodeToGui = HandleUtility.WorldToGUIPoint( _target.nodes[i] );
441             distance = Vector2.Distance( nodeToGui, mousePos );
442             
443             
if( distance < bestDistance )
444             {
445                 bestDistance = distance;
446                 index = i;
447             }
448         }
449         
450         
// make sure we are close enough to a node
451         
if( bestDistance < 10 )
452             
return index;
453         
return -1;
454     }
455     
456     
457     
private void closeRoute()
458     {
459         
// we will use the GoSpline class to handle the dirtywork of closing the path
460         
var path = new GoSpline( _target.nodes, _target.forceStraightLinePath );
461         path.closePath();
462         
463         _target.nodes = path.nodes;
464         
465         GUI.changed =
true;
466     }
467     
468     
469     
private void persistRouteToDisk( string path )
470     {
471         
var bytes = new List<byte>();
472         
473         
foreach( var vec in _target.nodes )
474         {
475             bytes.AddRange( System.BitConverter.GetBytes( vec.x ) );
476             bytes.AddRange( System.BitConverter.GetBytes( vec.y ) );
477             bytes.AddRange( System.BitConverter.GetBytes( vec.z ) );
478         }
479
480         File.WriteAllBytes( path, bytes.ToArray() );
481     }
482     
483     
484     
private void drawRoute()
485     {
486         
// if we are forcing straight lines just use this setup
487         
if( _target.forceStraightLinePath )
488         {
489             
// draw just the route here and optional arrows
490             
for( var i = 0; i < _target.nodes.Count; i++ )
491             {
492                 Handles.color = _target.pathColor;
493                 
if( i < _target.nodes.Count - 1 )
494                 {
495                     Handles.DrawLine( _target.nodes[i], _target.nodes[i +
1] );
496                     drawArrowBetweenPoints( _target.nodes[i], _target.nodes[i +
1] );
497                 }
498             }
499         }
500     }
501     
502     
#endregion
503     
504 }



Trò chơi Angry Birds trong UNITY Engine 31.717 lượt xem

Gõ tìm kiếm nhanh...